#include "znx_http_utils.h"
#include "http/znx_http_base.h"
#include "comm/znx_log.h"

znx_result_t
znx_http_readline(znx_http_readline_sm_t *readline_sm, znx_buf_t *buf)
{
    u_char  ch, *p;

    enum {
        SW_START = 0,
        SW_PROGRESS,
        SW_END
    };

    if (readline_sm->state == SW_END) {
        znx_http_readline_sm_reset(readline_sm);
    }

    int stat = readline_sm->state;
    for (p = buf->pos; p != buf->end; p++) {
        ch = *p;

        switch (stat) {
        case SW_START:
            if (ch == '\0') {
                return ZNX_FAILED;
            }

            stat = SW_PROGRESS;
            readline_sm->line.data = p;
            break;

        case SW_PROGRESS:
            if (ch == '\0') {
                return ZNX_FAILED;
            }

            if (ch == LF) {
                if (p - readline_sm->line.data < 1 || *(p - 1) != CR) {
                    break;
                }

                buf->pos = p + 1;
                readline_sm->line.len = (size_t)(p + 1 - readline_sm->line.data);
                readline_sm->state = SW_END;
                return ZNX_SUCCESS;
            }

            break;

        default:
            // unreachable.
            return ZNX_FAILED;
        }

        if ((size_t)(p + 1 - readline_sm->line.data) > readline_sm->max_line_size) {
            return ZNX_FAILED;
        }
    }

    buf->pos = p;
    readline_sm->state = stat;
    return ZNX_AGAIN;
}


znx_result_t
znx_http_chunk_size_parse(znx_http_chunk_sm_t *sm, znx_str_t *line)
{
    u_char   *p = line->data;
    u_char   *end = p + line->len;
    u_char   ch;

    if (p == end || *(end - 1) != LF) {
        return ZNX_FAILED;
    }

    if (sm->state == SW_CHUNK_START) {
        ch = (*p)|0x20;

        if (ch >= '0' && ch <= '9') {
            sm->size = ch - '0';
        } else if (ch >= 'a' && ch <= 'f') {
            sm->size = ch - 'a' + 10;
        } else {
            return ZNX_FAILED;
        }

        sm->state = SW_CHUNK_SIZE;
        p++;
    }

    if (sm->state != SW_CHUNK_SIZE) {
        ZNX_DEF_LOG_FATAL(NULL, "unreachable");
    }

    for (; p != end; p++) {
        ch = (*p)|0x20;

        if (ch >= '0' && ch <= '9') {
            sm->size = (sm->size * 16) + (ch - '0');
        } else if (ch >= 'a' && ch <= 'f') {
            sm->size = (sm->size * 16) + (ch - 'a' + 10);
        } else {
            /* chunk_extension, ignore */
            sm->remain = sm->size;
            sm->state = SW_CHUNK_DATA;
            return ZNX_SUCCESS;
        }
    }

    // unreachable.
    return ZNX_FAILED;
}